package com.bp.scoringgun.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bp.scoringgun.entity.Contri;
import com.bp.scoringgun.entity.Repo;
import com.bp.scoringgun.entity.User;
import com.bp.scoringgun.entity.UserContributionScore;
import com.bp.scoringgun.service.ContriService;
import com.bp.scoringgun.service.RepoService;
import com.bp.scoringgun.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class DataStatServiceImpl {

    @Autowired
    private RepoService repoService;  // 数据库访问接口
    @Autowired
    private UserService userService;
    @Autowired
    private ContriService contriService;
    /**
     * 统计并计算每个项目的总体分数
     * @return List<Repo> 更新后的 Repo 列表
     */
    public List<Repo> calculateProjectScores() {
        List<Repo> repos = repoService.list();  // 查询所有项目

        // 计算每个项目的分数
        for (Repo repo : repos) {
            // 计算各个维度的得分
            double influenceScore = calculateInfluenceScore(repo);
            double communityResponseScore = calculateCommunityResponseScore(repo);
            double developmentActivityScore = calculateDevelopmentActivityScore(repo);
            double teamHealthScore = calculateTeamHealthScore(repo);

            // 归一化各个维度得分
            repo.setInfluenceScore(influenceScore);
            repo.setCommunityResponseScore(communityResponseScore);
            repo.setDevelopmentActivityScore(developmentActivityScore);
            repo.setTeamHealthScore(teamHealthScore);
        }

        // 归一化所有项目的分数
        normalizeScores(repos);

        // 保存更新后的 Repo 对象
        repoService.updateBatchById(repos);  // 需要实现批量更新的方法

        return repos;  // 返回更新后的 Repo 列表
    }

    private double calculateInfluenceScore(Repo repo) {
        return 0.25 * repo.getNumWatch() +
                0.3 * repo.getNumFork() +
                0.5 * repo.getNumPR() +
                0.5 * repo.getNumIssue();
    }

    private double calculateCommunityResponseScore(Repo repo) {
        return 0.7 * repo.getNumSolveIssue() +
                0.3 * repo.getNumSolvePR();
    }

    private double calculateDevelopmentActivityScore(Repo repo) {
        return 0.8 * repo.getNumRelease();
    }

    private double calculateTeamHealthScore(Repo repo) {
        return 1.0 * repo.getNumMember();
    }

    /**
     * 对四个维度的分数进行归一化
     * @param repos 项目列表
     */
    private void normalizeScores(List<Repo> repos) {
        // 找到每个维度的最大值和最小值
        double maxInfluence = repos.stream().mapToDouble(Repo::getInfluenceScore).max().orElse(1.0);
        double minInfluence = repos.stream().mapToDouble(Repo::getInfluenceScore).min().orElse(0.0);

        double maxCommunity = repos.stream().mapToDouble(Repo::getCommunityResponseScore).max().orElse(1.0);
        double minCommunity = repos.stream().mapToDouble(Repo::getCommunityResponseScore).min().orElse(0.0);

        double maxDevelopment = repos.stream().mapToDouble(Repo::getDevelopmentActivityScore).max().orElse(1.0);
        double minDevelopment = repos.stream().mapToDouble(Repo::getDevelopmentActivityScore).min().orElse(0.0);

        double maxHealth = repos.stream().mapToDouble(Repo::getTeamHealthScore).max().orElse(1.0);
        double minHealth = repos.stream().mapToDouble(Repo::getTeamHealthScore).min().orElse(0.0);

        // 归一化处理
        for (Repo repo : repos) {
            // 归一化每个分数
            repo.setInfluenceScore(normalize(repo.getInfluenceScore(), minInfluence, maxInfluence));
            repo.setCommunityResponseScore(normalize(repo.getCommunityResponseScore(), minCommunity, maxCommunity));
            repo.setDevelopmentActivityScore(normalize(repo.getDevelopmentActivityScore(), minDevelopment, maxDevelopment));
            repo.setTeamHealthScore(normalize(repo.getTeamHealthScore(), minHealth, maxHealth));

            // 计算总体分数
            double overallScore = calculateOverallScore(repo);
            repo.setOverallScore(overallScore);
        }
    }

    private double normalize(double score, double min, double max) {
        double epsilon = 1e-10;  // 容差值
        if (Math.abs(max - min) < epsilon) {
            // 当 max 和 min 足够接近时，认为它们相等，直接返回 0
            return 0;
        }
        return (score - min) / (max - min);
    }

    private double calculateOverallScore(Repo repo) {
        return (
                0.3 * repo.getInfluenceScore() +
                        0.2 * repo.getCommunityResponseScore() +
                        0.15 * repo.getDevelopmentActivityScore() +
                        0.1 * repo.getTeamHealthScore()
        ) * (4.0 / 3.0);
    }

    /**
     * 计算每个人的总分，并将分数归一化
     */
    public void calculateAndNormalizeUserScores() {
        Map<Long, Map<Long, UserContributionScore>> userProjectScores = calculateAndNormalizeUserContributionScores();

        // 用于保存用户的总得分
        Map<Long, Double> userTotalScores = new HashMap<>();

        // 计算每个用户的总分
        for (Map.Entry<Long, Map<Long, UserContributionScore>> entry : userProjectScores.entrySet()) {
            Long userId = entry.getKey();
            Map<Long, UserContributionScore> projectScores = entry.getValue();

            double totalScore = 0.0;
            for (Map.Entry<Long, UserContributionScore> projectScore : projectScores.entrySet()) {
                Long repoId = projectScore.getKey();
                UserContributionScore contributionScore = projectScore.getValue();

                // 获取项目的权重参数（可以使用 RepoService 进行查询）
                double projectParameter = repoService.getProjectParameter(repoId);

                // 计算用户在该项目中的贡献得分 * (项目权重参数+1e-6)
                totalScore += contributionScore.getTotalScore() * (projectParameter+1e-6);
//                if(totalScore == 0){
//                    System.out.println(projectScores);
//                }
            }

            userTotalScores.put(userId, totalScore);
        }

        // 归一化用户总分
        double maxScore = userTotalScores.values().stream().max(Double::compareTo).orElse(1.0);
//        System.out.println(maxScore);
        List<Map.Entry<Long, Double>> sortedUserScores = userTotalScores.entrySet().stream()
                .map(entry -> Map.entry(entry.getKey(), 100 * entry.getValue() / maxScore))
                .sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))  // 按分数降序排序
                .collect(Collectors.toList());

        // 更新用户分数和排名
        for (int rank = 0; rank < sortedUserScores.size(); rank++) {
            Long userId = sortedUserScores.get(rank).getKey();
            double normalizedScore = sortedUserScores.get(rank).getValue();
            int userRank = rank + 1;
            // 更新用户的总分和排名到 User 表
            userService.updateUserScoreAndRank(userId, normalizedScore, userRank);
        }
        // 更新分数为0的排名为最后一名
        List<User> usersWithNoScore = userService.list(new QueryWrapper<User>().eq("score",0.0));
        for (User user : usersWithNoScore) {
            user.setUserRank((long) sortedUserScores.size());
        }
        userService.updateBatchById(usersWithNoScore);
    }

    /**
     * 计算每个人在每个项目上的贡献得分，并对每个维度进行归一化
     * @return Map<Long, Map<Long, UserContributionScore>> 每个用户在每个项目的贡献得分
     */
    public Map<Long, Map<Long, UserContributionScore>> calculateAndNormalizeUserContributionScores() {
        List<Contri> contributions = contriService.list();
        Map<Long, List<UserContributionScore>> projectContributionScores = new HashMap<>();

        // 先计算所有原始分数并存入map
        for (Contri contri : contributions) {
            Long userId = contri.getUserId();
            Long repoId = contri.getRepoId();

            // 计算各个维度原始得分
            double codeScore = calculateCodeScore(contri);
            double maintenanceScore = calculateMaintenanceScore(contri);
            double activityScore = calculateActivityScore(contri);

            // 保存每个人的贡献得分
            projectContributionScores
                    .computeIfAbsent(repoId, k -> new ArrayList<>())
                    .add(new UserContributionScore(userId, repoId, codeScore, maintenanceScore, activityScore));
        }

        // 对每个项目内的用户贡献得分进行归一化
        for (List<UserContributionScore> scores : projectContributionScores.values()) {
            normalizeUserScores(scores);
        }

        // 将结果按用户ID和项目ID存入最终map
        Map<Long, Map<Long, UserContributionScore>> userProjectScores = new HashMap<>();
        for (List<UserContributionScore> scores : projectContributionScores.values()) {
            for (UserContributionScore score : scores) {
                userProjectScores
                        .computeIfAbsent(score.getUserId(), k -> new HashMap<>())
                        .put(score.getRepoId(), score);
            }
        }

        return userProjectScores;
    }

    // 计算贡献分数
    private double calculateCodeScore(Contri contri) {
        return 5 * contri.getNumPush() + 7 * contri.getNumPR();
    }

    private double calculateMaintenanceScore(Contri contri) {
        return contri.getNumSolveIssue();
    }

    private double calculateActivityScore(Contri contri) {
        return contri.getNumCreate() + (contri.isOwner() ? 3 : 0);
    }

    // 对每个项目内的用户贡献得分进行归一化
    private void normalizeUserScores(List<UserContributionScore> scores) {
        double maxCodeScore = scores.stream().mapToDouble(UserContributionScore::getCodeScore).max().orElse(1.0);
        double maxMaintenanceScore = scores.stream().mapToDouble(UserContributionScore::getMaintenanceScore).max().orElse(1.0);
        double maxActivityScore = scores.stream().mapToDouble(UserContributionScore::getActivityScore).max().orElse(1.0);

        for (UserContributionScore score : scores) {
            // 检查 max 值是否为零，如果为零则设置分数为零，避免除零异常
            score.setCodeScore(maxCodeScore != 0 ? score.getCodeScore() / maxCodeScore : 0);
            score.setMaintenanceScore(maxMaintenanceScore != 0 ? score.getMaintenanceScore() / maxMaintenanceScore : 0);
            score.setActivityScore(maxActivityScore != 0 ? score.getActivityScore() / maxActivityScore : 0);

            // 计算总分
            double totalScore = 0.6 * score.getCodeScore() +
                    0.1 * score.getMaintenanceScore() +
                    0.3 * score.getActivityScore();
            score.setTotalScore(totalScore);
        }
    }

}
